package com.caisejingyu.whales.common.utils;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * 异常处理帮助类
 * <p>
 * Created by zhangdroid on 2017/7/21.
 */
@SuppressWarnings({"WeakerAccess", "unused"})
public class CrashHelper implements UncaughtExceptionHandler {

    private static CrashHelper mInstance = new CrashHelper();

    private UncaughtExceptionHandler mDefaultHandler;
    private Map<String, String> mLogInfo = new HashMap<>();
    public List<ICrashListener> mCrashListeners = new ArrayList<>();

    private CrashHelper() {}

    public static CrashHelper getInstance() {
        return mInstance;
    }

    public void init(Context paramContext) {
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    public void addListener(ICrashListener listener) {
        mCrashListeners.remove(listener);
        mCrashListeners.add(listener);
    }

    @SuppressWarnings("static-access")
    public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
        if (!handleException(paramThrowable) && mDefaultHandler != null) {
            // 如果自定义的没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(paramThread, paramThrowable);
        } else {
            try {
                // 如果处理了，让程序继续运行1.5秒再退出，保证文件保存并上传到服务器
                paramThread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            exitApp();
//            restartApp();
        }
    }
    
    /**
     * 退出程序
     */
    private void exitApp() {
        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(1);
    }
    
    /**
     * 重新启动
     */
    @SuppressWarnings("ConstantConditions")
    private void restartApp() {
        final Intent intent = AppUtil.getContext().getPackageManager().getLaunchIntentForPackage(AppUtil.getContext().getPackageName());
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        AppUtil.getContext().startActivity(intent);
        
        //结束进程之前可以把你程序的注销或者退出代码放在这段代码之前
        android.os.Process.killProcess(android.os.Process.myPid());
    }
    
    /**
     * handleException:{自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.}
     *
     * @return true:如果处理了该异常信息;否则返回false.
     */
    public boolean handleException(Throwable paramThrowable) {
        if (paramThrowable == null) {
            return false;
        }
        // 获取设备参数信息
        getDeviceInfo();
        // 保存日志文件
        saveCrashLogToFile(paramThrowable);

        // TODO 发生异常 通知出去。原来的全局日志需要
        ICrashListener[] crashListeners = mCrashListeners.toArray(new ICrashListener[mCrashListeners.size()]);
        for (ICrashListener listener : crashListeners) {
            listener.onCrash();
        }
        return true;
    }

    /**
     * getDeviceInfo:{获取设备参数信息}
     */
    public void getDeviceInfo() {
        try {
            // 获得包管理器
            PackageManager mPackageManager = AppUtil.getContext().getPackageManager();
            // 得到该应用的信息，即主Activity
            PackageInfo mPackageInfo = mPackageManager.getPackageInfo(AppUtil.getContext().getPackageName(), PackageManager.GET_ACTIVITIES);
            if (mPackageInfo != null) {
                String versionName = mPackageInfo.versionName == null ? "null" : mPackageInfo.versionName;
                String versionCode = mPackageInfo.versionCode + "";
                Date d = new Date();
                SimpleDateFormat sd = new SimpleDateFormat("yyyyMMdd_HH-mm-ss", Locale.getDefault());
                String date = sd.format(d);
                mLogInfo.put("versionName", versionName);
                mLogInfo.put("versionCode", versionCode);
                mLogInfo.put("date", date);
            }
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        // 反射机制
        Field[] mFields = Build.class.getDeclaredFields();
        // 迭代Build的字段key-value 此处的信息主要是为了在服务器端手机各种版本手机报错的原因
        for (Field field : mFields) {
            try {
                field.setAccessible(true);
                mLogInfo.put(field.getName(), field.get("").toString());
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * saveCrashLogToFile:{将崩溃的Log保存到本地} 可拓展，将Log上传至指定服务器路径
     */
    private void saveCrashLogToFile(Throwable paramThrowable) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : mLogInfo.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key).append("=").append(value).append("\r\n");
        }
        Writer mWriter = new StringWriter();
        PrintWriter mPrintWriter = new PrintWriter(mWriter);
        paramThrowable.printStackTrace(mPrintWriter);
        paramThrowable.printStackTrace();
        Throwable mThrowable = paramThrowable.getCause();
        // 迭代栈队列把所有的异常信息写入writer中
        while (mThrowable != null) {
            mThrowable.printStackTrace(mPrintWriter);
            // 换行 每个个异常栈之间换行
            mPrintWriter.append("\r\n");
            mThrowable = mThrowable.getCause();
        }
        // 记得关闭
        mPrintWriter.close();
        String mResult = mWriter.toString();
        sb.append(mResult);

        // 存放到Android/data/包名/根目录下
        File file = AppUtil.getContext().getExternalFilesDir(null);
        if (file == null) {
            return;
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HH-mm-ss", Locale.getDefault());
        String fileName = file.getPath() + File.separator + "crash_" + simpleDateFormat.format(new Date(System.currentTimeMillis())) + ".txt";
        FileUtil.writeString(fileName, sb.toString(), false);
    }


    /**
     * 异常监听器
     *
     */
    public interface ICrashListener {
        void onCrash();
    }


}
